#! /usr/bin/env python

'''Creates an HDF5 file of dust events. The file does not have a /user/experiment
heirarchy. It just has a list of the dust events in the root group.

Mike Dorey  2011-04-07
'''


import os
import optparse
import config_file
import database_access
import dust_events_hdf5


# defaults
FILE_VERSION = 'B1'
DB_SERVER = 'localhost'
DB_DATABASE = 'ccldas'
DB_DEFAULTS_FILENAME = os.path.join(os.environ['HOME'], '.my.cnf')


def get_criteria(cursor, config):
    '''Returns a criteria dictionary needed to call 
    database_access.fetch_dust_events_that_meet_criteria.
    The entries in the dictionary are derived from the 
    configuration dictionary.

    Argument
        cursor - a MySQLdb.cursors.DictCursor cursor
        config - dictionary that holds the configuration
    '''
    criteria = {}
    timestamp_groups = get_timestamp_groups(cursor, config)
    if len(timestamp_groups) > 0:
        criteria['timestamp_groups'] = get_timestamp_groups(cursor, config)
    upper_and_lower_velocities_criteria = get_velocities_criteria(config)
    if len(upper_and_lower_velocities_criteria) > 0:
        criteria['lower_and_upper_velocities'] = upper_and_lower_velocities_criteria
    upper_and_lower_charges_criteria = get_charges_criteria(config)
    if len(upper_and_lower_charges_criteria) > 0:
        criteria['lower_and_upper_charges'] = upper_and_lower_charges_criteria
    upper_and_lower_masses_criteria = get_masses_criteria(config)
    if len(upper_and_lower_masses_criteria) > 0:
        criteria['lower_and_upper_masses'] = upper_and_lower_masses_criteria
    if config.has_key('selection'):
        criteria['selection'] = config['selection']
    return criteria

  
def get_timestamp_groups(cursor, config):
    '''Returns a list of lists. Like so.
    [[(start time, stop time), (start time, stop time), ...], 
     [(start time, stop time), (start time, stop time), ...], 
                                                        ...]]
    Each of the inner lists hold tuples that are time ranges to
    be added to the criteria that selects dust events.

    Argument
        cursor - a MySQLdb.cursors.DictCursor cursor
        config - dictionary that holds the configuration
    '''
    timestamp_groups = []
    if config.has_key('user'):
        user_boundaries = database_access.timestamp_boundaries_for_user(cursor, 
                   config['user'])
        timestamp_groups.append(user_boundaries)
    if config.has_key('experiment'):
        exp_boundaries = database_access.timestamp_boundaries_for_experiment(
               cursor, config['experiment'])
        timestamp_groups.append(exp_boundaries)
    config_boundaries = database_access.timestamp_boundaries_from_config(config)
    if config_boundaries is not None:
        timestamp_groups.append([config_boundaries])
    if config.has_key('chemical comp') and config.has_key('batch'):
        dust_type = database_access.DustType(config['chemical comp'],
             config['batch'])
        dust_type_boundaries = \
                    database_access.timestamp_boundaries_for_dust_type(cursor, 
                             dust_type)
        timestamp_groups.append(dust_type_boundaries)
    return timestamp_groups


def get_velocities_criteria(config):
    '''Returns a list of tuples (lower velocity, upper velocity) that are 
    specified in the configuration dictionary.

    Argument
        config - configuration dictionary
    '''
    if config.has_key('lower velocity'):
        if config.has_key('upper velocity'):
            return [(config['lower velocity'], config['upper velocity'])]
        else:
            # has lower velocity but no upper velocity
            return [(config['lower velocity'], 1.0e300)]
    else:
        if config.has_key('upper velocity'):
            # has no lower velocity but does have an upper velocity
            return [(0.0, config['upper velocity'])]
        else:
            # has no lower velocity or upper velocity
            return []


def get_charges_criteria(config):
    '''Returns a list of tuples (lower charge, upper charge) that are 
    specified in the configuration dictionary.

    Argument
        config - configuration dictionary
    '''
    if config.has_key('lower charge'):
        if config.has_key('upper charge'):
            return [(config['lower charge'], config['upper charge'])]
        else:
            # has lower charge but no upper charge
            return [(config['lower charge'], 1.0e300)]
    else:
        if config.has_key('upper charge'):
            # has no lower charge but does have an upper charge
            return [(-1.0e300, config['upper charge'])]
        else:
            # has no lower charge or upper charge
            return []


def get_masses_criteria(config):
    '''Returns a list of tuples (lower mass, upper mass) that are 
    specified in the configuration dictionary.

    Argument
        config - configuration dictionary
    '''
    if config.has_key('lower mass'):
        if config.has_key('upper mass'):
            return [(config['lower mass'], config['upper mass'])]
        else:
            # has lower mass but no upper mass
            return [(config['lower mass'], 1.0e300)]
    else:
        if config.has_key('upper mass'):
            # has no lower mass but does have an upper mass
            return [(0.0, config['upper mass'])]
        else:
            # has no lower mass or upper mass
            return []


def get_options_args():
    '''Returns a tuple of (options, user name, output filename), which are the options and
    arguments specified on the command line.
    '''
    usage = 'Usage: %prog [options] <configuration file name> <output file name>'
    option_parser = optparse.OptionParser(usage=usage)
    option_parser.add_option("-s", "--server", dest="db_server", 
                             default=DB_SERVER,
                             help="name of the database server")
    option_parser.add_option("-d", "--database", dest="database", 
                             default=DB_DATABASE,
                             help="name of the database")
    option_parser.add_option("-v", "--version", dest="file_version", 
                             default=FILE_VERSION,
                             help="version of the output file")
    option_parser.add_option("-f", "--db_defaults_filename", 
                             dest="db_defaults_filename",
                             default=DB_DEFAULTS_FILENAME,
                             help="name of the database defaults file")
    (options, args) = option_parser.parse_args()
    assert len(args) == 2, 'Improper usage. Did not specify both the configuration and the output filename.'
    return (options, args[0], args[1])


def main():
    '''The main driver.  '''
    (options, config_filename, output_filename) = get_options_args()
    hdf5_file = dust_events_hdf5.open_hdf5_file(output_filename, 
                   options.file_version)
    try:
        (connection, cursor) = database_access.get_connection_and_cursor(
                  options.db_server, options.database, 
                  options.db_defaults_filename)
        try:
            config = config_file.parse(config_filename)
            criteria = get_criteria(cursor, config)
            events = database_access.fetch_dust_events_that_meet_criteria(
                                    cursor, criteria)
            dust_events_hdf5.create_dust_events_dataset(hdf5_file, config, 
                 events)
        finally:
            connection.close()
    finally:
        hdf5_file.close()


if __name__ == "__main__":
    main()
